﻿using System;
using LogicGates;

namespace MipsProject
{
    #region ALU Types
    public enum OpCode
    {
        AND,
        OR,
        ADD,
        SUB,
        SLT,
        NOR
    }
    #endregion

    public class ALU
    {
        #region Private Variables
        bool zeroFlag;
        bool overflowFlag;
        bool carryFlag;
        int aluSize;

        AluXBit mAlu;
        bus aBus;
        bus bBus;
        
        #endregion

        #region Properties
        public bool ZeroFlag { get { return zeroFlag; } }
        public bool OverflowFlag { get { return overflowFlag; } }
        public bool CarryFlag { get { return carryFlag; } }
        #endregion

        #region Constructors
        public ALU(bool zeroFlag, bool overflowFlag, bool carryFlag, int aluSize)
        {
            this.zeroFlag = zeroFlag;
            this.overflowFlag = overflowFlag;
            this.carryFlag = carryFlag;
            this.aluSize = aluSize;
            mAlu = new AluXBit(aluSize);
            aBus = new bus(aluSize);
            bBus = new bus(aluSize);
        }

        public ALU() : this(false, false, false, 4)
        {
            
        }

        public ALU(int aluSize)
            : this(false, false, false, aluSize)
        {

        }

        #endregion

        public int Execute(OpCode opCode, int input1, int input2)
        {
            int result = 0;

            switch (opCode)
            {
                case OpCode.AND:
                    //result = input1 & input2;
                    result = this.executeInternal(0, 0, 0, input1, input2);
                    break;
                case OpCode.OR:
                    //result = input1 | input2;
                    result = this.executeInternal(1, 0, 0, input1, input2);
                    break;
                case OpCode.ADD:
                    //result = input1 + input2;
                    result = this.executeInternal(2, 0, 0, input1, input2);
                    break;
                case OpCode.SUB:
                    //result = input1 - input2;
                    result = this.executeInternal(2, 1, 1, input1, input2);
                    break;
                case OpCode.SLT:
                    //result = input1 < input2 ? 1 : 0;
                    break;
                case OpCode.NOR:
                    //result = ~(input1 | input2);
                    break;
                default:
                    throw new ApplicationException("Unsupported OpCode specified");
            }

            return result;
        }

        private int executeInternal(int opCode, int bInvert, int carryIn, int input1, int input2)
        {
            aBus.setBus(input1);
            bBus.setBus(input2);

            mAlu.opCode = opCode;
            mAlu.A = aBus;
            mAlu.B = bBus;
            mAlu.bInvert = bInvert;
            mAlu.carryIn = carryIn;

            return mAlu.result.getBusDecimal();     
        }
    }
}
